/*
 *  policy - Part of the Policy Server Application
 *  Copyright (C) 2009 Liam Lowey
 *  
 *      http://policyserver.turnitin.co.uk/
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License. 
 *  You may obtain a copy of the License at 
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0 
 *  
 *  Unless required by applicable law or agreed to in writing, software 
 *  distributed under the License is distributed on an "AS IS" BASIS, 
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 *  See the License for the specific language governing permissions and 
 *  limitations under the License. 
 */

using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace policyserver
{
  interface Observer
  {
    void addConnection( Connection obs );
  }


  class Listener
  {
    private const int JOIN_TIMEOUT = 20000; /*ms*/

    private Observer _o;
    private Thread _thread;
    private bool _running = true;
    private object _thisLock = new object();
    private TcpListener _listener;
    private IPAddress _wakeUpIp;
    private int _serverPort;
    private byte[] _payload;

    public Listener( Observer o, IPAddress bindTo, int port, byte[] payload )
    {
      _o = o;
      _payload = payload;

      if( bindTo == IPAddress.Any )
      {
        _wakeUpIp = new IPAddress( new byte[] { 127, 0, 0, 1 } );
      }
      else
      {
        _wakeUpIp = bindTo;
      }

      _serverPort = port;

      _listener = new TcpListener( bindTo, port );

      _listener.Start();

      _thread = new Thread( this.run );

      _thread.Start();
    }


    ~Listener()
    {
      _listener.Stop();
    }


    public void stop()
    {
      if( null != _thread )
      {
        bool didStop = false;

        lock( _thisLock )
        {
          _running = false;

          try
          {
            Socket wakeupSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IPv4 );
            wakeupSocket.Connect( _wakeUpIp, _serverPort );
          }
          catch
          {
            didStop = true;
            try { _listener.Stop(); }
            catch { }
          }
        }

        _thread.Join( JOIN_TIMEOUT );

        if( !didStop )
        {
          try { _listener.Stop(); }
          catch { }
        }
      }
    }


    private bool isRunning
    {
      get { lock( _thisLock ) { return _running; } }
    }


    public void run()
    {
      trace( "Listener.run() - Starting listener for port: {0}.", _serverPort );

      try
      {
        while( isRunning )
        {
          Socket newSocket = _listener.AcceptSocket();

          // test because we may have been woken up by stop
          if( isRunning )
          {
            _o.addConnection( new Connection( newSocket, _payload ) );
          }
        }
      }
      catch( SocketException )
      {

      }

      trace( "Listener.run() - Exiting." );
    }

    private static void trace( string format, params object[] args )
    {
      System.Console.WriteLine( format, args );
    }
  }
}
